package view_server

import (
	"bigdevops/src/cache"
	"bigdevops/src/common"
	"bigdevops/src/config"
	"bigdevops/src/models"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	"go.uber.org/zap"
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/kubernetes"
	"os"
	"sigs.k8s.io/yaml"
	"sort"
	"strconv"
	"strings"
	"time"
)

// 先进行一些校验
// 再生成Deployment配置
// instance 可以继承 app的：containerCore字段 instance 可以不填：不填就得用app的
// 如果instance 填了，就用自己的
// 保存数据库 并 调用k8s接口创建资源

// 权限校验
// 创建和更新都要传入 app的id ，需要校验，这个用户有没有app创建 instance的权限
// 换句话说：这个用户有没有app 绑定服务树节点 或者它的父级的权限
func createK8sInstanceOne(c *gin.Context) {
	//kset *kubernetes.Clientset, podName string, containerName string, ns string string
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	var reqObj models.K8sInstance
	err := c.ShouldBindJSON(&reqObj)
	if err != nil {
		sc.Logger.Error("解析k8sCreateInstanceOne配置请求失败", zap.Any("k8s配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}
	// TODO 下面设置人和校验权限 在noauth验证的时候会报错，因为没有过jwt模块，你自己要注释一下
	// 设置人
	userName := c.MustGet(common.GIN_CTX_JWT_USER_NAME).(string)
	dbUser, err := models.GetUserByUserName(userName)
	if err != nil {
		sc.Logger.Error("通过token解析到的userName去数据库中找User失败",
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("通过token解析到的userName去数据库中找User失败:%v", err.Error()), c)
		return
	}

	reqObj.UserID = dbUser.ID

	// 前转后
	reqObj.FillDefaultData()

	// 根据服务树校验权限
	err = reqObj.FillFrontAllData()
	if err != nil {
		msg := fmt.Sprintf("FillFrontAllData报错:%v", err.Error())
		sc.Logger.Error(msg)
		common.ReqBadFailWithMessage(msg, c)
		return
	}

	// 在创建节点前要校验权限
	pass, _ := streeNodeOpsAdminPermissionCheck(reqObj.K8sAppObj.StreeNodeObj, c)
	if !pass {
		sc.Logger.Error("服务树节点权限校验未通过", zap.Any("reqNode", reqObj.K8sAppObj.TreeNodeId), zap.Error(err))
		common.Req403WithWithMessage("服务树节点权限校验未通过", c)
		return

	}
	//  到这里

	// 在这里校验字段，是否必填，范围是否正确
	err = validate.Struct(reqObj)
	if err != nil {

		// 这里为什么要判断错误是否是 ValidationErrors
		if errors1, ok := err.(validator.ValidationErrors); ok {
			common.ReqBadFailWithWithDetailed(

				gin.H{
					"翻译前": err.Error(),
					"翻译后": errors1.Translate(trans),
				},
				"请求出错",
				c,
			)
			return
		}
		common.ReqBadFailWithMessage(err.Error(), c)
		return

	}

	searchCluster := reqObj.K8sAppObj.Cluster
	reqObj.Cluster = reqObj.K8sAppObj.Cluster
	//searchName := reqObj.Name

	if searchCluster == "" {
		msg := "集群名称未传入"
		sc.Logger.Error(msg)
		common.ReqBadFailWithMessage(msg, c)
		return
	}

	dbObj, err := models.GetK8sClusterByName(searchCluster)
	if err != nil {
		sc.Logger.Error("根据name找k8s集群错误", zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	reqObj.ClusterObj = dbObj
	// 从cache中通过 集群的id 拿到集群的clientSet
	kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
	kSet := kc.GetClusterClientSetById(dbObj.ID)
	if kSet == nil {
		msg := fmt.Sprintf("根据id找k8s集群kset缓存错误")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}

	// 生成Deployment 和svc
	dep, err := reqObj.GenDeployment()
	if err != nil {
		msg := fmt.Sprintf("生成Deployment和service配置错误:%v", err.Error())
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}

	// 创建Deployment svc 或者更新Deployment svc

	err = createK8sInstanceOneApplyDeployment(c, kSet, reqObj, dep)
	if err != nil {

		msg := fmt.Sprintf("创建Deployment或更新错误:%v", err.Error())
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}

	// 下面级联创建
	err = reqObj.CreateOne()
	if err != nil {
		sc.Logger.Error("新增k8s-实例数据库失败", zap.Any("k8s集群配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}
	common.OkWithData(reqObj, c)
	//common.OkWithMessage("创建成功", c)
}

func createK8sInstanceOneInner(c *gin.Context, reqObj models.K8sInstance) string {
	//kset *kubernetes.Clientset, podName string, containerName string, ns string string
	//sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	// TODO 下面设置人和校验权限 在noauth验证的时候会报错，因为没有过jwt模块，你自己要注释一下
	// 设置人
	userName := c.MustGet(common.GIN_CTX_JWT_USER_NAME).(string)
	dbUser, err := models.GetUserByUserName(userName)
	if err != nil {
		msg := fmt.Sprintf("通过token解析到的userName去数据库中找User失败:%v", err.Error())

		return msg
	}

	reqObj.UserID = dbUser.ID

	// 前转后
	reqObj.FillDefaultData()

	// 根据服务树校验权限
	err = reqObj.FillFrontAllData()
	if err != nil {
		msg := fmt.Sprintf("FillFrontAllData报错:%v", err.Error())
		return msg
	}

	// 在创建节点前要校验权限
	pass, _ := streeNodeOpsAdminPermissionCheck(reqObj.K8sAppObj.StreeNodeObj, c)
	if !pass {
		msg := fmt.Sprintf("服务树节点权限校验未通过", zap.Any("reqNode", reqObj.K8sAppObj.TreeNodeId))
		return msg

	}
	//  到这里

	// 在这里校验字段，是否必填，范围是否正确
	err = validate.Struct(reqObj)
	if err != nil {
		return err.Error()
	}

	searchCluster := reqObj.K8sAppObj.Cluster
	reqObj.Cluster = reqObj.K8sAppObj.Cluster
	//searchName := reqObj.Name

	if searchCluster == "" {
		msg := "集群名称未传入"
		return msg
	}

	dbObj, err := models.GetK8sClusterByName(searchCluster)
	if err != nil {
		msg := fmt.Sprintf("根据name找k8s集群错误", zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		return msg
	}

	reqObj.ClusterObj = dbObj
	// 从cache中通过 集群的id 拿到集群的clientSet
	kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
	kSet := kc.GetClusterClientSetById(dbObj.ID)
	if kSet == nil {
		msg := fmt.Sprintf("根据id找k8s集群kset缓存错误")
		return msg
	}

	// 生成Deployment 和svc
	dep, err := reqObj.GenDeployment()
	if err != nil {
		msg := fmt.Sprintf("生成Deployment和service配置错误:%v", err.Error())
		return msg
	}

	// 创建Deployment svc 或者更新Deployment svc

	err = createK8sInstanceOneApplyDeployment(c, kSet, reqObj, dep)
	if err != nil {

		msg := fmt.Sprintf("创建Deployment或更新错误:%v", err.Error())
		return msg
	}

	// 下面级联创建
	err = reqObj.CreateOne()
	if err != nil {
		msg := fmt.Sprintf("新增k8s-实例数据库失败", zap.Any("k8s集群配置", reqObj), zap.Error(err))
		return msg
	}
	return ""
}

func updateK8sInstanceOneInner(c *gin.Context, reqObj models.K8sInstance) string {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	// 前转后
	reqObj.FillDefaultData()
	err := reqObj.FillFrontAllData()
	if err != nil {
		msg := fmt.Sprintf("FillFrontAllData报错:%v", err.Error())
		sc.Logger.Error(msg)
		return msg
	}

	// 根据服务树校验权限
	/*权限校验开始*/

	//
	// 在创建节点前要校验权限
	pass, _ := streeNodeOpsAdminPermissionCheck(reqObj.K8sAppObj.StreeNodeObj, c)
	if !pass {

		msg := fmt.Sprintf("服务树节点权限校验未通过", zap.Any("reqNode", reqObj.K8sAppObj.TreeNodeId))
		sc.Logger.Error(msg)
		return msg

	}
	/*权限校验结束*/

	searchCluster := reqObj.Cluster
	//searchName := reqObj.Name

	if searchCluster == "" {
		msg := "集群名称未传入"
		sc.Logger.Error(msg)
		return msg
	}

	dbObj, err := models.GetK8sClusterByName(searchCluster)
	if err != nil {
		msg := fmt.Sprintf("根据name找k8s集群错误 :%v", searchCluster)
		sc.Logger.Error(msg, zap.Error(err))
		return msg
	}

	reqObj.ClusterObj = dbObj
	// 从cache中通过 集群的id 拿到集群的clientSet
	kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
	kSet := kc.GetClusterClientSetById(dbObj.ID)
	if kSet == nil {
		msg := fmt.Sprintf("根据id找k8s集群kset缓存错误")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		return msg
	}

	// 生成Deployment 和svc
	dep, err := reqObj.GenDeployment()
	if err != nil {
		msg := fmt.Sprintf("生成Deployment和service配置错误:%v", err.Error())
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		return msg
	}

	// 创建Deployment svc 或者更新Deployment svc

	err = createK8sInstanceOneApplyDeployment(c, kSet, reqObj, dep)
	if err != nil {

		msg := fmt.Sprintf("创建Deployment或更新错误:%v", err.Error())
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		return msg
	}

	// 下面级联创建
	err = reqObj.UpdateOne()
	if err != nil {
		msg := fmt.Sprintf("更新k8s-实例数据库失败 %v", err.Error())
		sc.Logger.Error(msg, zap.Error(err))
		return msg
	}
	return ""
}

func updateK8sInstanceOne(c *gin.Context) {
	//kset *kubernetes.Clientset, podName string, containerName string, ns string string
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	var reqObj models.K8sInstance
	err := c.ShouldBindJSON(&reqObj)
	if err != nil {
		sc.Logger.Error("解析k8sCreateInstanceOne配置请求失败", zap.Any("k8s配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}
	// TODO 下面设置人和校验权限 在noauth验证的时候会报错，因为没有过jwt模块，你自己要注释一下

	// 在这里校验字段，是否必填，范围是否正确
	err = validate.Struct(reqObj)
	if err != nil {

		// 这里为什么要判断错误是否是 ValidationErrors
		if errors1, ok := err.(validator.ValidationErrors); ok {
			common.ReqBadFailWithWithDetailed(

				gin.H{
					"翻译前": err.Error(),
					"翻译后": errors1.Translate(trans),
				},
				"请求出错",
				c,
			)
			return
		}
		common.ReqBadFailWithMessage(err.Error(), c)
		return

	}

	_, err = models.GetK8sInstanceById(int(reqObj.ID))
	if err != nil {
		sc.Logger.Error("根据id找k8s实例配置错误", zap.Any("k8s集群配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 前转后
	reqObj.FillDefaultData()
	err = reqObj.FillFrontAllData()
	if err != nil {
		msg := fmt.Sprintf("FillFrontAllData报错:%v", err.Error())
		sc.Logger.Error(msg)
		common.ReqBadFailWithMessage(msg, c)
		return
	}

	// 根据服务树校验权限
	/*权限校验开始*/

	//
	// 在创建节点前要校验权限
	pass, _ := streeNodeOpsAdminPermissionCheck(reqObj.K8sAppObj.StreeNodeObj, c)
	if !pass {
		sc.Logger.Error("服务树节点权限校验未通过", zap.Any("reqNode", reqObj.K8sAppObj.TreeNodeId), zap.Error(err))
		common.Req403WithWithMessage("服务树节点权限校验未通过", c)
		return

	}
	/*权限校验结束*/

	searchCluster := reqObj.Cluster
	//searchName := reqObj.Name

	if searchCluster == "" {
		msg := "集群名称未传入"
		sc.Logger.Error(msg)
		common.ReqBadFailWithMessage(msg, c)
		return
	}

	dbObj, err := models.GetK8sClusterByName(searchCluster)
	if err != nil {
		sc.Logger.Error("根据name找k8s集群错误", zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	reqObj.ClusterObj = dbObj
	// 从cache中通过 集群的id 拿到集群的clientSet
	kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
	kSet := kc.GetClusterClientSetById(dbObj.ID)
	if kSet == nil {
		msg := fmt.Sprintf("根据id找k8s集群kset缓存错误")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}

	// 生成Deployment 和svc
	dep, err := reqObj.GenDeployment()
	if err != nil {
		msg := fmt.Sprintf("生成Deployment和service配置错误:%v", err.Error())
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}

	// 创建Deployment svc 或者更新Deployment svc

	err = createK8sInstanceOneApplyDeployment(c, kSet, reqObj, dep)
	if err != nil {

		msg := fmt.Sprintf("创建Deployment或更新错误:%v", err.Error())
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}

	// 下面级联创建
	err = reqObj.UpdateOne()
	if err != nil {
		sc.Logger.Error("更新k8s-实例数据库失败", zap.Any("k8s集群配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}
	common.OkWithData(reqObj, c)
	//common.OkWithMessage("更新成功", c)
}

func createOrUpdateK8sInstanceOneForCICD(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	var reqObj models.K8sInstance
	err := c.ShouldBindJSON(&reqObj)
	if err != nil {
		sc.Logger.Error("解析createOrUpdateK8sInstanceOneForCicd配置请求失败", zap.Any("k8s配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 进行是否存在的判断
	dbObj, err := models.GetK8sInstanceByK8sAppIdAndName(reqObj.K8sAppID, reqObj.Name)
	msg := "创建成功"
	if err == nil {
		reqObj.ID = dbObj.ID
		updateK8sInstanceOneInner(c, reqObj)
		msg = "更新成功"
		common.OkWithMessage(msg, c)
		return
	} else {
		createK8sInstanceOneInner(c, reqObj)
		common.OkWithMessage(msg, c)
		return
	}

}

// 创建或者更新Deployment公共方法
func createK8sInstanceOneApplyDeployment(c *gin.Context, kSet *kubernetes.Clientset, reqObj models.K8sInstance, dep *appsv1.Deployment) error {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	dep.ManagedFields = nil
	tmp, _ := yaml.Marshal(dep)
	os.WriteFile(fmt.Sprintf("%v/k8s_deploy_%v.yaml", sc.LocalYamlDir, dep.Name), tmp, 0644)

	// 先去k8s 查询，有就更新，没有就创建

	ctx1, canFun1 := common.GenTimeoutContext(reqObj.ClusterObj.ActionTimeoutSeconds)
	defer canFun1()
	depInK8s, err := kSet.AppsV1().Deployments(dep.Namespace).Get(ctx1, dep.Name, metav1.GetOptions{})
	if err != nil {
		// 非not found的错误 -->  网络错误
		if !errors.IsNotFound(err) {
			msg := fmt.Sprintf("创建或更新svc的data前置获取deployment对象错误:%v", err.Error())
			err = fmt.Errorf("%v %w", msg, err)
			sc.Logger.Error(msg, zap.Any("k8s集群配置", reqObj.Cluster), zap.Error(err))
			//common.FailWithMessage(msg, c)
			//c.Abort()
			return err

		}
		// 没有这个对象 ，创建即可
		ctx2, canFun2 := common.GenTimeoutContext(reqObj.ClusterObj.ActionTimeoutSeconds)
		defer canFun2()
		dep, err = kSet.AppsV1().Deployments(dep.Namespace).Create(ctx2, dep, metav1.CreateOptions{})
		if err != nil {
			msg := fmt.Sprintf("创建deploy错误:%v", err.Error())
			err = fmt.Errorf("%v %w", msg, err)
			sc.Logger.Error(msg, zap.Any("k8s集群配置", reqObj.Cluster), zap.Error(err))
			return err
		}
		return nil
	}

	depInK8s.Spec = dep.Spec

	ctx3, canFun3 := common.GenTimeoutContext(reqObj.ClusterObj.ActionTimeoutSeconds)
	defer canFun3()
	dep, err = kSet.AppsV1().Deployments(dep.Namespace).Update(ctx3, depInK8s, metav1.UpdateOptions{})
	if err != nil {
		msg := fmt.Sprintf("更新deployment对象错误:%v", err.Error())
		err = fmt.Errorf("%v %w", msg, err)
		sc.Logger.Error(msg, zap.Any("k8s集群配置", reqObj.Cluster), zap.Error(err))
		return err
	}
	return nil
}

// 创建或者更新svc公共方法
func createK8sInstanceOneApplyService(c *gin.Context, kSet *kubernetes.Clientset, clusterObj *models.K8sCluster, svc *corev1.Service) error {

	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	// 先去k8s 查询，有就更新，没有就创建
	svc.ManagedFields = nil
	tmp, _ := yaml.Marshal(svc)

	os.WriteFile(fmt.Sprintf("%v/k8s_service_%v.yaml", sc.LocalYamlDir, svc.Name), tmp, 0644)

	ctx1, canFun1 := common.GenTimeoutContext(clusterObj.ActionTimeoutSeconds)
	defer canFun1()
	svcInK8s, err := kSet.CoreV1().Services(svc.Namespace).Get(ctx1, svc.Name, metav1.GetOptions{})
	if err != nil {
		// 非not found的错误 -->  网络错误
		if !errors.IsNotFound(err) {
			msg := fmt.Sprintf("创建或更新svc的data前置获取svc对象错误:%v", err.Error())
			err = fmt.Errorf("%v %w", msg, err)
			sc.Logger.Error(msg, zap.Any("k8s集群配置", clusterObj.Name), zap.Error(err))
			//common.FailWithMessage(msg, c)
			//c.Abort()
			return err

		}
		// 没有这个对象 ，创建即可
		ctx2, canFun2 := common.GenTimeoutContext(clusterObj.ActionTimeoutSeconds)
		defer canFun2()
		svc, err = kSet.CoreV1().Services(svc.Namespace).Create(ctx2, svc, metav1.CreateOptions{})
		if err != nil {
			msg := fmt.Sprintf("创建svc错误:%v", err.Error())
			err = fmt.Errorf("%v %w", msg, err)
			sc.Logger.Error(msg, zap.Any("k8s集群配置", clusterObj.Name), zap.Error(err))
			return err
		}
		return nil
	}

	// 更新
	svcInK8s.Spec = svc.Spec
	ctx3, canFun3 := common.GenTimeoutContext(clusterObj.ActionTimeoutSeconds)
	defer canFun3()
	svc, err = kSet.CoreV1().Services(svc.Namespace).Update(ctx3, svcInK8s, metav1.UpdateOptions{})
	if err != nil {
		msg := fmt.Sprintf("更新svc对象错误:%v", err.Error())
		err = fmt.Errorf("%v %w", msg, err)
		sc.Logger.Error(msg, zap.Any("k8s集群配置", clusterObj.Name), zap.Error(err))
		return err
	}
	return nil
}

type K8sBatchDeleteInstance struct {
	Cluster string   `json:"cluster"`
	Names   []string `json:"names"`
	Ids     []int    `json:"ids"`
}

func batchDeleteK8sInstance(c *gin.Context) {
	//kset *kubernetes.Clientset, podName string, containerName string, ns string string
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	var reqObj K8sBatchDeleteInstance
	err := c.ShouldBindJSON(&reqObj)
	if err != nil {
		sc.Logger.Error("解析批量删除Deployment请求失败", zap.Any("k8s配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 在这里校验字段，是否必填，范围是否正确
	err = validate.Struct(reqObj)
	if err != nil {

		// 这里为什么要判断错误是否是 ValidationErrors
		if errors, ok := err.(validator.ValidationErrors); ok {
			common.ReqBadFailWithWithDetailed(

				gin.H{
					"翻译前": err.Error(),
					"翻译后": errors.Translate(trans),
				},
				"请求出错",
				c,
			)
			return
		}
		common.ReqBadFailWithMessage(err.Error(), c)
		return

	}

	for _, id := range reqObj.Ids {

		dbInstance, err := models.GetK8sInstanceById(id)
		if err != nil {
			msg := fmt.Sprintf("根据id找instance配置错误")
			sc.Logger.Error(msg, zap.Any("k8s集群配置", id), zap.Error(err))
			common.FailWithMessage(msg, c)
			return
		}
		err = dbInstance.FillFrontAllData()
		if err != nil {
			msg := fmt.Sprintf("dbInstance.FillFrontAllData报错:%v", err.Error())
			sc.Logger.Error(msg)
			common.ReqBadFailWithMessage(msg, c)
			return
		}

		// 根据服务树校验权限
		/*权限校验开始*/

		//
		// 在创建节点前要校验权限
		//pass, _ := streeNodeOpsAdminPermissionCheck(dbInstance.K8sAppObj.StreeNodeObj, c)
		//if !pass {
		//	msg := fmt.Sprintf("服务树节点权限校验未通过:%v", dbInstance.K8sAppObj.StreeNodeObj.NodePath)
		//	sc.Logger.Error(msg, zap.Any("reqNode", dbInstance.K8sAppObj.TreeNodeId), zap.Error(err))
		//	common.Req403WithWithMessage(msg, c)
		//	return
		//
		//}
		/*权限校验结束*/

		searchCluster := dbInstance.Cluster

		if searchCluster == "" {
			msg := "集群名称未传入"
			sc.Logger.Error(msg)
			common.ReqBadFailWithMessage(msg, c)
			return
		}

		// 从cache中通过 集群的id 拿到集群的clientSet
		kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
		kSet := kc.GetClusterClientSetById(dbInstance.ClusterObj.ID)
		if kSet == nil {
			msg := fmt.Sprintf("根据id找k8s集群kset缓存错误")
			sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
			common.FailWithMessage(msg, c)
			return
		}

		depName := fmt.Sprintf("%s-%s", dbInstance.K8sAppObj.Name, dbInstance.Name)

		err = dbInstance.DeleteOne()
		if err != nil {
			msg := fmt.Sprintf("删除k8s-instance数据库错误 :%v:%v", depName, err.Error())
			sc.Logger.Error(msg)
			common.ReqBadFailWithMessage(msg, c)
			return
		}

		// 先删除Deployment 再删除数据库
		ctx1, canFun1 := common.GenTimeoutContext(dbInstance.ClusterObj.ActionTimeoutSeconds)
		defer canFun1()

		err = kSet.AppsV1().Deployments(dbInstance.K8sAppObj.Namespace).Delete(ctx1, depName, metav1.DeleteOptions{})
		if err != nil {
			msg := fmt.Sprintf("删除Deployment错误 :%v:%v", depName, err.Error())
			sc.Logger.Error(msg)
			common.ReqBadFailWithMessage(msg, c)
			return
		}

	}

	common.OkWithMessage("删除成功", c)

}

func batchRestartK8sInstance(c *gin.Context) {
	//kset *kubernetes.Clientset, podName string, containerName string, ns string string
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	var reqObj K8sBatchDeleteInstance
	err := c.ShouldBindJSON(&reqObj)
	if err != nil {
		sc.Logger.Error("解析批量删除Deployment请求失败", zap.Any("k8s配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 在这里校验字段，是否必填，范围是否正确
	err = validate.Struct(reqObj)
	if err != nil {

		// 这里为什么要判断错误是否是 ValidationErrors
		if errors, ok := err.(validator.ValidationErrors); ok {
			common.ReqBadFailWithWithDetailed(

				gin.H{
					"翻译前": err.Error(),
					"翻译后": errors.Translate(trans),
				},
				"请求出错",
				c,
			)
			return
		}
		common.ReqBadFailWithMessage(err.Error(), c)
		return

	}

	for _, id := range reqObj.Ids {
		dbInstance, err := models.GetK8sInstanceById(id)
		if err != nil {
			msg := fmt.Sprintf("根据id找instance配置错误")
			sc.Logger.Error(msg, zap.Any("k8s集群配置", id), zap.Error(err))
			common.FailWithMessage(msg, c)
			return
		}
		err = dbInstance.FillFrontAllData()
		if err != nil {
			msg := fmt.Sprintf("dbInstance.FillFrontAllData报错:%v", err.Error())
			sc.Logger.Error(msg)
			common.ReqBadFailWithMessage(msg, c)
			return
		}

		// 从cache中通过 集群的id 拿到集群的clientSet
		kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
		kSet := kc.GetClusterClientSetById(dbInstance.ClusterObj.ID)
		if kSet == nil {
			msg := fmt.Sprintf("根据id找k8s集群kset缓存错误")
			sc.Logger.Error(msg, zap.Any("k8s集群配置", dbInstance.ClusterObj.Name), zap.Error(err))
			common.FailWithMessage(msg, c)
			return
		}

		// 根据服务树校验权限
		/*权限校验开始*/

		//
		// 在创建节点前要校验权限
		pass, _ := streeNodeOpsAdminPermissionCheck(dbInstance.K8sAppObj.StreeNodeObj, c)
		if !pass {
			msg := fmt.Sprintf("服务树节点权限校验未通过:%v", dbInstance.K8sAppObj.StreeNodeObj.NodePath)
			sc.Logger.Error(msg, zap.Any("reqNode", dbInstance.K8sAppObj.TreeNodeId), zap.Error(err))
			common.Req403WithWithMessage(msg, c)
			return
		}
		//}
		/*权限校验结束*/
		// 直接patch
		//ctx1, canFun1 := common.GenTimeoutContext(dbObj.ActionTimeoutSeconds)
		//defer canFun1()
		depName := fmt.Sprintf("%s-%s", dbInstance.K8sAppObj.Name, dbInstance.Name)
		ns := dbInstance.K8sAppObj.Namespace
		// 重启搭一个updateAt时间，patch

		//_,err = kSet.AppsV1().Deployments(ns).Get(ctx1, depName, metav1.GetOptions{})
		//if err != nil {
		//	msg := fmt.Sprintf("重启Deployment前get错误 :%v:%v", depName, err.Error())
		//	sc.Logger.Error(msg)
		//	common.ReqBadFailWithMessage(msg, c)
		//	return
		//}

		ctx2, canFun2 := common.GenTimeoutContext(dbInstance.ClusterObj.ActionTimeoutSeconds)
		defer canFun2()

		data := fmt.Sprintf(`{"spec": {"template": {"metadata": {"annotations": {"bigdevops/restartedAt": "%s"}}}}}`, time.Now().Format("2006-01-02 15:04:05"))

		_, err = kSet.AppsV1().Deployments(ns).Patch(ctx2, depName, types.StrategicMergePatchType, []byte(data), metav1.PatchOptions{})
		if err != nil {
			msg := fmt.Sprintf("重启patch-Deployment错误:ns:%v name:%v %v", ns, depName, err.Error())
			sc.Logger.Error(msg, zap.Any("k8s集群配置", dbInstance.ClusterObj.Name), zap.Error(err))
			common.FailWithMessage(msg, c)
			return
		}

	}

	common.OkWithMessage("重启成功", c)

}

// 根据一个k8s app找到下属的 所有instance 列表
func getK8sInstanceByApp(c *gin.Context) {

	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	currentPage, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
	sortFiled := c.DefaultQuery("field", "")
	orderType := c.DefaultQuery("order", "")

	// 集群的名称

	searchCluster := c.DefaultQuery("cluster", "")
	searchApp := c.DefaultQuery("app", "")
	//searchName := c.DefaultQuery("name", "")

	if searchCluster == "" {
		msg := "集群名称未传入"
		sc.Logger.Error(msg)
		common.ReqBadFailWithMessage(msg, c)
		return
	}

	// 拿到k8s集群对象

	dbObj, err := models.GetK8sClusterByName(searchCluster)
	if err != nil {
		sc.Logger.Error("根据name找k8s集群错误", zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 从cache中通过 集群的id 拿到集群的clientSet
	kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
	kSet := kc.GetClusterClientSetById(dbObj.ID)
	if kSet == nil {
		msg := fmt.Sprintf("根据id找k8s集群kset缓存错误")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 拿到app的对象
	dbApp, err := models.GetK8sAppByName(searchApp)
	if err != nil {
		sc.Logger.Error("根据name找k8s-app错误", zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}
	dbInstances, err := models.GetK8sInstanceByK8sAppId(int(dbApp.ID))
	if err != nil {

		msg := fmt.Sprintf("根据k8sAppId找k8s-instance数据库错误")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 进行搜索
	filterResults := dbInstances

	//startIndex, endIndex
	startIndex := (pageSize) * (currentPage - 1)
	endIndex := startIndex + pageSize
	if endIndex > len(filterResults) {
		endIndex = len(filterResults)
	}

	// 转化一下
	pageResult := filterResults[startIndex:endIndex]

	// 去k8s中查询一下状态 ，这里使用list请求一次，再遍历对比即可

	ctx1, canFun1 := common.GenTimeoutContext(dbObj.ActionTimeoutSeconds)
	defer canFun1()
	labelSelector, _ := fields.ParseSelector(fmt.Sprintf("%v=%v", common.K8S_APP_LABEL_APP_NAME, dbApp.Name))
	depItems, err := kSet.AppsV1().Deployments(dbApp.Namespace).List(ctx1, metav1.ListOptions{
		LabelSelector: labelSelector.String(),
	})
	if err != nil {
		msg := fmt.Sprintf("根据LabelSelector找k8s-deployment错误:%v", labelSelector.String())
		sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}
	depMap := map[string]*appsv1.Deployment{}
	for _, dep := range depItems.Items {
		depMap[dep.Name] = &dep
	}
	// 遍历pageResult 匹配状态
	for i := 0; i < len(pageResult); i++ {
		one := pageResult[i]
		one.FillFrontAllData()
		depName := fmt.Sprintf("%s-%s", one.K8sAppObj.Name, one.Name)
		dep := depMap[depName]
		if dep == nil {
			continue
		}

		desiredReplicas := int(*dep.Spec.Replicas)
		readyReplicas := int(dep.Status.ReadyReplicas)

		one.ReadyStatus = fmt.Sprintf("%d/%d", readyReplicas, desiredReplicas)
	}

	// 排序
	if sortFiled == "name" {

		sort.SliceStable(pageResult, func(i, j int) bool {
			if orderType == "ascend" {
				return pageResult[i].Name < pageResult[j].Name
			} else {
				return pageResult[i].Name > pageResult[j].Name
			}

		})
	}

	resp := &ResponseResourceCommon{
		//Total: models.GetK8sClusterCount(), 因为是带了查询条件的 所以 count 不能是all 而是 过滤后的total

		Total: len(filterResults),
		Items: pageResult,
	}

	common.OkWithDetailed(resp, "ok", c)
}

func getK8sInstanceList(c *gin.Context) {

	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	currentPage, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "100"))

	offset := 0
	limit := 0
	limit = pageSize
	if currentPage > 1 {
		offset = (currentPage - 1) * limit
	}
	// 这里只能新增 不能去掉，因为是 多种资源共用的query
	searchUserID := c.DefaultQuery("UserID", "")

	searchUserIDInt, _ := strconv.Atoi(searchUserID)
	searchName := c.DefaultQuery("name", "")

	// 查询条件
	//searchCluster := c.DefaultQuery("cluster", "")
	searchAppId, _ := strconv.Atoi(c.DefaultQuery("appId", ""))

	// 	数据库中拿到所有的menu列表

	var (
		objs []*models.K8sInstance
		err  error
	)
	if searchAppId > 0 {
		objs, err = models.GetK8sInstanceByClusterAndAppId(searchAppId)
		if err != nil {
			sc.Logger.Error("去数据库中拿所有的k8s实例配置错误",
				zap.Error(err),
			)
			common.ReqBadFailWithMessage(fmt.Sprintf("去数据库中拿所有的k8s实例配置错误:%v", err.Error()), c)
			return
		}

	} else {
		objs, err = models.GetK8sInstanceAll()
		if err != nil {
			sc.Logger.Error("去数据库中拿所有的k8s实例配置错误",
				zap.Error(err),
			)
			common.ReqBadFailWithMessage(fmt.Sprintf("去数据库中拿所有的k8s实例配置错误:%v", err.Error()), c)
			return
		}

	}
	// 从cache中通过 集群的id 拿到集群的clientSet
	kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)

	allIds := []int{}
	//// 遍历 role 准备menuIds 列表
	for _, obj := range objs {
		obj := obj

		// 在这里处理查询
		if searchName != "" && !strings.Contains(obj.Name, searchName) {
			continue
		}

		// 在这里处理查询
		if searchUserID != "" && int(obj.UserID) != searchUserIDInt {
			continue
		}
		allIds = append(allIds, int(obj.ID))

		//role.MenuIds = menuIds
	}

	if len(allIds) == 0 {
		common.OkWithDetailed(allIds, "ok", c)
		return
	}
	objs, err = models.GetK8sInstanceByIdsWithLimitOffset(allIds, limit, offset)
	if err != nil {
		sc.Logger.Error("limit-offset去数据库中拿所有的任务错误",
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("limit-offset去数据库中拿所有的脚本模板错误:%v", err.Error()), c)
		return
	}

	// 进行判断 指定appId查询或者查询多个
	if searchAppId > 0 {
		searchCluster := objs[0].Cluster
		// 获取一下Deployment的状态

		dbK8sApp, err := models.GetK8sAppById(int(searchAppId))
		if err != nil {
			sc.Logger.Error("根据name找k8s集群错误", zap.Any("k8s集群配置", searchCluster), zap.Error(err))
			common.FailWithMessage(err.Error(), c)
			return
		}
		dbObj, err := models.GetK8sClusterByName(searchCluster)
		if err != nil {
			sc.Logger.Error("根据name找k8s集群错误", zap.Any("k8s集群配置", searchCluster), zap.Error(err))
			common.FailWithMessage(err.Error(), c)
			return
		}

		kSet := kc.GetClusterClientSetById(dbObj.ID)
		if kSet == nil {
			msg := fmt.Sprintf("根据id找k8s集群kset缓存错误")
			sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
			common.FailWithMessage(err.Error(), c)
			return
		}
		ctx1, canFun1 := common.GenTimeoutContext(dbObj.ActionTimeoutSeconds)
		defer canFun1()
		labelSelector, _ := fields.ParseSelector(fmt.Sprintf("%v=%v", common.K8S_APP_LABEL_APP_NAME, dbK8sApp.Name))
		depItems, err := kSet.AppsV1().Deployments(dbK8sApp.Namespace).List(ctx1, metav1.ListOptions{
			LabelSelector: labelSelector.String(),
		})
		if err != nil {

			msg := fmt.Sprintf("根据LabelSelector找k8s-deployment错误:%v", labelSelector.String())
			sc.Logger.Error(msg, zap.Any("k8s集群配置", searchCluster), zap.Error(err))
			common.FailWithMessage(err.Error(), c)
			return
		}
		instanceMap := map[string]*models.K8sInstance{}
		for _, obj := range objs {
			obj := obj
			obj.FillFrontAllData()
			instanceMap[fmt.Sprintf("%s-%s", dbK8sApp.Name, obj.Name)] = obj
		}

		for _, dep := range depItems.Items {

			dbInstance := instanceMap[dep.Name]
			if dbInstance == nil {
				continue
			}

			desiredReplicas := int(*dep.Spec.Replicas)
			readyReplicas := int(dep.Status.ReadyReplicas)

			dbInstance.ReadyStatus = fmt.Sprintf("%d/%d", readyReplicas, desiredReplicas)
		}
		resp := &ResponseResourceCommon{
			//Total: models.GetK8sAppCount(), 因为是带了查询条件的 所以 count 不能是all 而是 过滤后的total

			Total: len(allIds),
			Items: objs,
		}

		common.OkWithDetailed(resp, "ok", c)
		return
	}

	// 走到这里说查询所有
	for _, obj := range objs {
		obj := obj
		obj.FillFrontAllData()

		depName := fmt.Sprintf("%s-%s", obj.K8sAppObj.Name, obj.Name)

		kSet := kc.GetClusterClientSetById(obj.ClusterObj.ID)
		if kSet == nil {
			msg := fmt.Sprintf("根据id找k8s集群kset缓存错误")
			sc.Logger.Error(msg, zap.Any("k8s集群配置", obj.ClusterObj.ID), zap.Error(err))
			common.FailWithMessage(err.Error(), c)
			return
		}
		ctx2, canFun2 := common.GenTimeoutContext(obj.ClusterObj.ActionTimeoutSeconds)
		defer canFun2()
		dep, err := kSet.AppsV1().Deployments(obj.K8sAppObj.Namespace).Get(ctx2, depName, metav1.GetOptions{})
		if err != nil {

			msg := fmt.Sprintf("根据depName找k8s-deployment错误:%v", depName)
			sc.Logger.Error(msg, zap.Any("depName", depName), zap.Error(err))
			common.FailWithMessage(err.Error(), c)
			return
		}
		desiredReplicas := int(*dep.Spec.Replicas)
		readyReplicas := int(dep.Status.ReadyReplicas)

		obj.ReadyStatus = fmt.Sprintf("%d/%d", readyReplicas, desiredReplicas)

	}
	resp := &ResponseResourceCommon{
		//Total: models.GetK8sAppCount(), 因为是带了查询条件的 所以 count 不能是all 而是 过滤后的total

		Total: len(allIds),
		Items: objs,
	}

	common.OkWithDetailed(resp, "ok", c)
	return

}

func getK8sInstanceOne(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	// 校验一下 menu字段
	id := c.Param("id")

	// 先 去db中根据id找到这个user
	intVar, _ := strconv.Atoi(id)
	dbObj, err := models.GetK8sInstanceById(intVar)
	if err != nil {
		sc.Logger.Error("根据id找k8s应用错误", zap.Any("任务实例", id), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}
	dbObj.FillFrontAllData()

	common.OkWithData(dbObj, c)
}
